package dwarffortress.loaders;

import dwarffortress.data.*;
import dwarffortress.io.DFInputStream;
import dwarffortress.io.DFInflaterStream;
import dwarffortress.tools.Logger;

import java.io.*;

public class DFWorldDatLoader {


    public static DFWorld loadDFWorld(File dir, Logger logger) throws IOException {

        InputStream in_file = new DFInflaterStream(new BufferedInputStream(new FileInputStream(new File(dir, "world.dat"))));

        DFInputStream in = new DFInputStream(in_file);



        int version = in.readInt();
        if (version != 0x04F4) {
            throw new StrongAssumptionException(String.format("Incorrect version at the file header : %8x", version));
        }

        try {
            return new ReflectiveLoader().loadObject(DFWorld.class, in);
        } catch (IllegalAccessException e) {
            throw new IOException("");
        } catch (InstantiationException e) {
            throw new IOException("");
        } finally {
            log(logger, Logger.DEBUG, "Expecting Region Save " );

            String string = in.readDFString();
            if (!"*START REGION SAVE*".equals(string)) {
                throw new StrongAssumptionException(String.format("Finished loading in wrong place"));
            }
            log(logger, Logger.DEBUG, "Done reading world " );

        }
//        return oldReadWorld(logger, in);
    }

    private static DFWorld oldReadWorld(Logger logger, DFInputStream in) throws IOException {
        DFWorld result = new DFWorld();
        result.f000_char_zero = in.readShort();
        result.f001_int = in.readInt();

        result.f002_int = in.readInt();
        result.f003_int = in.readInt();
        result.f004_int = in.readInt();

        result.f005_int = in.readInt();

        result.f006_int = in.readInt();
        result.f007_int = in.readInt();
        result.f008_int = in.readInt();
        result.f009_int = in.readInt();

        result.f010_int = in.readInt();
        result.f011_int = in.readInt();
        result.f012_numberOfSites = in.readInt();
        result.f013_int = in.readInt();


        byte[] b;


        result.f014_byte = in.readByte();
        if (result.f014_byte != 1) {
            throw new StrongAssumptionException(String.format("Byte before world name is not one : %d", result.f014_byte));
        }
        result.f015_worldname = in.readDFString();

        result.f016_char_zero_maybeString = in.readShort();
        result.f018_worldNamePart1 = in.readInt();
        result.f019_worldNamePart2 = in.readInt();
        result.f020_worldNamePart3 = in.readInt();
        result.f021_worldNamePart4 = in.readInt();
        result.f022_worldNamePart5 = in.readInt();
        result.f023_worldNamePart6 = in.readInt();
        result.f024_worldNamePart7 = in.readInt();

        result.field2_8_int  = in.readInt();


        result.field2_9_int = in.readInt();
        result.field2_10_short = in.readShort();
        result.field2_11_short = in.readShort();
        result.field2_12_short = in.readShort();
        result.field2_13_int = in.readInt();
        result.field2_14_short = in.readShort();
        result.field2_15_byte  = in.readByte();
        result.field2_16_short = in.readShort();
        result.field2_17_int = in.readInt();
        result.field2_18_int = in.readInt();
        result.field2_19_int = in.readInt();


        result.f036_worldTranslatedName = in.readDFString();
        result.rawWoodMatgloss = in.readStringList();
        result.rawStoneMatgloss = in.readStringList();
        result.rawMetalMatgloss = in.readStringList();
        result.rawPlantMatgloss = in.readStringList();
        result.rawBodyIDs = in.readStringList();
        result.rawBodyGlossIDs = in.readStringList();
        result.rawCreatureIDs = in.readStringList();
        result.rawItemIDs = in.readStringList();
        result.rawEntityIDs = in.readStringList();
        result.rawWordIDs = in.readStringList();
        result.rawSymbolIDs = in.readStringList();
        result.rawLanguageIDs = in.readStringList();
        result.rawColorIDs = in.readStringList();
        result.rawShapeIDs = in.readStringList();
        result.rawReactionIDs = in.readStringList();

        result.field3 = in.readInt();
        result.field4 = in.readInt();

        result.entityIDs = in.readIntList();
        result.otherIDs = in.readIntList();

        result.field5 = in.readInt();
        if (result.field5 != 0) {
            throw new StrongAssumptionException(String.format("Unknown field 5 in World is not 0: %d", result.field5));
        }
        result.field6 = in.readInt();
        log(logger, Logger.DEBUG, "Field 6 : " + result.field6 );

        result.field7 = in.readInt();
        if (result.field7 != 0) {
            throw new StrongAssumptionException(String.format("Unknown field 7 in World is not 0: %d", result.field7));
        }
        log(logger, Logger.DEBUG, "Entities count : " + result.entityIDs.length);
        result.entities = new DFEntity[result.entityIDs.length];
        for (int index = 0; index < result.entities.length; index++) {
            log(logger, Logger.DEBUG, "Reading Entity #" + index);
            result.entities[index] = readEntity(in, logger);
        }
        log(logger, Logger.DEBUG, "Expecting Region Save " );

        String string = in.readDFString();
        if (!"*START REGION SAVE*".equals(string)) {
            throw new StrongAssumptionException(String.format("Finished loading in wrong place"));
        }
        log(logger, Logger.DEBUG, "Done reading world " );

        in.close();
        return result;
    }

    private static void log(Logger logger, int level, String s) {
        if (null!= logger) {
            logger.log(level, s);
        }
    }

    private static DFEntity readEntity(DFInputStream in, Logger logger) throws IOException {
        DFEntity result;
        result = newReadEntity(in);
//        result = oldReadEntity(in, logger);
        return result;
    }

    private static DFEntity oldReadEntity(DFInputStream in, Logger logger) throws IOException {
        DFEntity result = new DFEntity();
        result.f000_kind = in.readShort();
        result.f001_id = in.readInt();
        result.f002_typeRef = in.readDFString();
        result.f003_short = in.readShort();
        result.f004_short = in.readShort();
        result.f005_maybeUnitId = in.readInt();
        result.f006_int = in.readInt();
        result.f007_short_zero = in.readShort();
        if (result.f007_short_zero != 0) {
            throw new StrongAssumptionException("Field u4 in Entity is not zero : " + result.f007_short_zero);
        }
        result.f008_byte_one = in.readByte();
        result.f009_someName = in.readDFString();

        result.f010_short_zero_maybe_string = in.readShort();
        if (result.f010_short_zero_maybe_string != 0) {
            throw new StrongAssumptionException("Field u6 in Entity is not zero : " + result.f010_short_zero_maybe_string);
        }

        result.f011_worldNamePart1 = in.readInt();
        result.f012_worldNamePart2 = in.readInt();
        result.f013_worldNamePart3 = in.readInt();
        result.f014_worldNamePart4 = in.readInt();
        result.f015_worldNamePart5 = in.readInt();
        result.f016_worldNamePart6 = in.readInt();
        result.f017_worldNamePart7 = in.readInt();

        result.f018_int_zero = in.readInt();
        if (result.f018_int_zero != 0) {
            throw new StrongAssumptionException("Field u7 in Entity is not zero : " + result.f018_int_zero);
        }

        result.f019_short = in.readShort();
        result.f020_short = in.readShort();
        result.f021_short = in.readShort();
        result.f022_short = in.readShort();
        result.f023_short = in.readShort();
        result.f024_someIdMaybeCiv = in.readInt();
        result.f025_short = in.readShort();
        result.f026_civilizationCreatureRef = in.readShort();
        result.f027_int = in.readInt();
        result.f028_short_list = in.readShortList();
        result.f029_short_list = in.readShortList();
        result.f030_short_list = in.readShortList();
        result.f031_short_list = in.readShortList();
        result.f032_short_list = in.readShortList();
        result.f033_int_zero = in.readInt();
        if (result.f033_int_zero != 0) {
            throw new StrongAssumptionException("Field u20 in Entity is not zero : " + result.f033_int_zero);
        }
        result.f034_int_zero = in.readInt();
        if (result.f034_int_zero != 0) {
            throw new StrongAssumptionException("Field u21 in Entity is not zero : " + result.f034_int_zero);
        }
        result.f035_short_list = in.readShortList();
        result.f036_short_list = in.readShortList();
        result.f037_short_list = in.readShortList();
        result.f038_short_list = in.readShortList();
        result.f039_short_list = in.readShortList();
        result.f040_short_list = in.readShortList();
        result.f041_short_list = in.readShortList();
        result.f042_short_list = in.readShortList();
        result.f043_short_list = in.readShortList();
        result.f044_short_list = in.readShortList();
        result.f045_short_list = in.readShortList();
        result.f046_short_list = in.readShortList();
        result.f047_short_list = in.readShortList();
        result.f048_short_list_creature_ids = in.readShortList();
        result.f049_short_list = in.readShortList();
        result.f050_short_list = in.readShortList();

        result.f051_short_list_length_4_with_one_element_N = in.readShortList();
        result.f052_short_list = in.readShortList();
        result.f053_short_list = in.readShortList();
        result.f054_short_list = in.readShortList();

        result.f055_short_list = in.readShortList();
        result.f056_short_list = in.readShortList();

        result.f057_short_list = in.readShortList();
        result.f058_short_list = in.readShortList();

        result.f059_int_zero = in.readInt();
        if (result.f059_int_zero != 0) {
            throw new StrongAssumptionException("Field u46 in Entity is not zero : " + result.f059_int_zero);
        }

        result.f060_short_list = in.readShortList();
        result.f061_short_list = in.readShortList();
        result.f062_short_list = in.readShortList();
        result.f063_short_list = in.readShortList();
        log(logger, Logger.DEBUG, "50 fields ok");

        result.f064_short_list_with_creature_ids = in.readShortList();

        result.f065_int_zero = in.readInt();
        if (result.f065_int_zero != 0) {
            throw new StrongAssumptionException("Field u52 in Entity is not zero : " + result.f065_int_zero);
        }
        result.f066_int_zero = in.readInt();
        if (result.f066_int_zero != 0) {
            throw new StrongAssumptionException("Field u53 in Entity is not zero : " + result.f066_int_zero);
        }

        result.f067_short_list = in.readShortList();
        result.f068_short_list = in.readShortList();

        result.f069_short_list = in.readShortList();
        result.f070_short_list = in.readShortList();

        result.f071_short_ff = in.readShort();
        result.f072_short = in.readShort();
        result.f073_short_ff = in.readShort();
        result.f074_short = in.readShort();
        result.f075_short_ff = in.readShort();
        result.f076_short = in.readShort();

        result.f077_short_list = in.readShortList();
        result.f078_short_list = in.readShortList();
        result.f079_short_list = in.readShortList();
        result.f080_short_list = in.readShortList();
        result.f081_short_list = in.readShortList();
        result.f082_short_list = in.readShortList();
        result.f083_short_list = in.readShortList();
        result.f084_short_list = in.readShortList();
        result.f085_short_list = in.readShortList();
        result.f086_short_list = in.readShortList();
        result.f087_short_list = in.readShortList();
        result.f088_short_list = in.readShortList();
        result.f089_short_list = in.readShortList();

        result.f090_short_list = new short[]{-239};

        result.f090_short_list = in.readShortList();
        result.f091_short_list = in.readShortList();
        result.f092_short_list = in.readShortList();
        result.f093_short_list = in.readShortList();
        result.f094_short_list = in.readShortList();
        result.f095_short_list = in.readShortList();
        result.f096_short_list = in.readShortList();
        result.f097_short_list = in.readShortList();

        result.f098_short_list = in.readShortList();
        result.f099_short_list = in.readShortList();

        result.f100_short_list = in.readShortList();
        result.f101_short_list = in.readShortList();

        result.f102_short_list = in.readShortList();
        log(logger, Logger.DEBUG, "90 fields ok");
        result.f103_short_list = in.readShortList();
        result.f104_int_list = in.readIntList();

        result.f105_byte_list = in.readByteList();
        result.f106_byte_list = in.readByteList();

        result.f107_byte_list = in.readByteList();
        result.f108_byte_list = in.readByteList();

        result.f109_byte_list = in.readByteList();
        result.f110_byte_list = in.readByteList();

        result.f111_int_zero = in.readInt();
        if (result.f111_int_zero != 0) {
            throw new StrongAssumptionException("Field u99 in Entity is not zero : " + result.f111_int_zero);
        }
        result.f112_int_zero = in.readInt();
        if (result.f112_int_zero != 0) {
            throw new StrongAssumptionException("Field u100 in Entity is not zero : " + result.f112_int_zero);
        }
        log(logger, Logger.DEBUG, "Reading field 101");
        int u101_size = in.readInt();
        in.checkArrayLength(u101_size);
        result.f113_structureList = new DFUnkStruct01[u101_size];
        for (int i = 0; i < u101_size; i++) {
            result.f113_structureList[i] = readUnkStruct01(in);
        }
        log(logger, Logger.DEBUG, "Reading field 102");
        int u102_size = in.readInt();
        in.checkArrayLength(u102_size);
        result.f114_structureList = new DFUnkStruct01[u102_size];
        for (int i = 0; i < u102_size; i++) {
            result.f114_structureList[i] = readUnkStruct01(in);
        }
        log(logger, Logger.DEBUG, "Reading field 103");
        result.f115_int_list = in.readIntList();
        result.f116_listOfSomeRefs = in.readIntList();
        result.f117_short_list = in.readShortList();
        result.f118_int_list = in.readIntList();
        result.f119_short_list = in.readShortList();


        result.f120_byte_list = in.readByteList();
        result.f121_byte_list = in.readByteList();
        result.f122_byte_list = in.readByteList();
        result.f123_byte_list = in.readByteList();
        result.f124_short_list = in.readShortList();


        result.f125_int_one = in.readInt();
        result.f126_short = in.readShort();

        log(logger, Logger.DEBUG, "Reading field 115");

        int u115_counter = in.readInt();
        in.checkArrayLength(u115_counter);
        result.f127_structureList = new DFUnkStruct02[u115_counter];
        for (int i = 0; i < result.f127_structureList.length; i++) {
            result.f127_structureList[i] = readUnkStruct02(in);
        }
        log(logger, Logger.DEBUG, "Reading field 116");
        result.f128_short = in.readShort();
        result.f129_int = in.readInt();

        result.f131_byte = in.readByte();
        result.f132_byte = in.readByte();
        result.f133_byte = in.readByte();
        result.f134_byte = in.readByte();
        result.f136_short = in.readShort();
        result.f137_short = in.readShort();
        result.f138_short = in.readShort();
        result.f139_short = in.readShort();
        result.f140_int_list = in.readIntList();
        result.f141_int_list = in.readIntList();
        result.f142_byte_list = in.readByteList();
        result.f143_somethingToDoWithWorship = in.readIntList();
        result.f144_somethingToDoWithWorship = in.readIntList();
        log(logger, Logger.DEBUG, "Reading field 133");
        int u133 = in.readInt();
        in.checkArrayLength(u133);
        result.f145_structureList = new DFUnkStruct04[u133];
        for (int i = 0; i < u133; i++) {
            result.f145_structureList[i] = readUnkStruct04(in);
        }
        log(logger, Logger.DEBUG, "Reading field 134");
        int u134 = in.readInt();
        in.checkArrayLength(u134);
        result.f146_structureList = new DFUnkStruct05[u134];
        for (int i = 0; i < u134; i++) {
            result.f146_structureList[i] = readUnkStruct05(in);
        }
        log(logger, Logger.DEBUG, "Reading field 135");
        result.f147_short = in.readShort();
        result.f148_short_list = in.readShortList();
        result.f149_int_list = in.readIntList();
        return result;
    }

    private static DFEntity newReadEntity(DFInputStream in) throws IOException {
        try {
            return new ReflectiveLoader().loadObject(DFEntity.class, in);
        } catch (IllegalAccessException e) {
            throw new IOException("");
        } catch (InstantiationException e) {
            throw new IOException("");
        }
    }

    private static DFUnkStruct05 readUnkStruct05(DFInputStream in) throws IOException {
        DFUnkStruct05 result = new DFUnkStruct05();
        result.f000_id = in.readInt(); //id
        result.f001_short = in.readShort();
        result.f002_short = in.readShort();
        result.f003_short = in.readShort();
        result.f004_int_list = in.readIntList();
        result.f005_int_list = in.readIntList();
        result.f006_int = in.readInt();
        return result;
    }

    private static DFUnkStruct04 readUnkStruct04(DFInputStream in) throws IOException {
        DFUnkStruct04 result = new DFUnkStruct04();
        result.f000_short_list = in.readShortList();
        result.f001_short_list = in.readShortList();
        result.f002_int = in.readInt();
        result.f003_int = in.readInt();
        result.f004_int_list = in.readIntList();
        result.f005_short = in.readShort();
        result.f006_short = in.readShort();
        return result;
    }

    private static DFUnkStruct01 readUnkStruct01(DFInputStream in) throws IOException {
        DFUnkStruct01 result = new DFUnkStruct01();
        result.f000_low = in.readShort();
        result.f001_civ_ref = in.readInt();
        result.f002_high = in.readShort();
        return result;
    }

    private static DFUnkStruct02 readUnkStruct02(DFInputStream in) throws IOException {
        DFUnkStruct02 result = new DFUnkStruct02();
        result.f000_short = in.readShort();
        result.f001_short_list = in.readShortList();
        result.f002_short_list = in.readShortList();
        int count = in.readInt();
        in.checkArrayLength(count);
        result.f003_structureList = new DFUnkStruct03[count];
        for (int i = 0; i < result.f003_structureList.length; i++) {
            result.f003_structureList[i] = readUnkStruct03(in);
        }
        return result;
    }

    private static DFUnkStruct03 readUnkStruct03(DFInputStream in) throws IOException {
        DFUnkStruct03 result = new DFUnkStruct03();
        result.f000_byte = in.readByte();
        result.f001_byte = in.readByte();
        result.f002_short = in.readShort();
        result.f003_int = in.readInt();
        result.f004_short = in.readShort();
        result.f005_short = in.readShort();
        result.f006_short = in.readShort();
        result.f007_short = in.readShort();
        return result;
    }
}